
package spreadsheet.ui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.filechooser.FileFilter;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import spreadsheet.JDOMXMLReader;
import spreadsheet.SpreadSheet;
import spreadsheet.SpreadSheetCell;

/**
 * Main window of the project
 */
public class MainWindow extends JFrame {
    
    private JLabel lblStatus = new JLabel( "Status: " );
    private JTextField txtFormula = new JTextField();
    private Table tblSpreadsheet = new Table();
    private JButton btnOpen = new JButton( "Open File" );
    private JButton btnSave = new JButton( "Save File" );
    private JButton btnResize = new JButton( "Resize Table" );
    private File fileCurrent;
    
    /**
     * Constructor for MainWindow
     */
    public MainWindow() {
        super( "SpreadSheet" );
        
//        start of layout section
        setLayout( new GridBagLayout() );
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets( 5, 5, 5, 5 );
        add( new JLabel( "Formula:" ), gbc );
        gbc.gridx = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 0.1;
        add( txtFormula, gbc );
        gbc.fill = GridBagConstraints.NONE;
        gbc.weightx = 0;
        gbc.gridx = 2;
        add( btnOpen, gbc );
        gbc.gridx = 3;
        add( btnSave, gbc );
        gbc.gridx = 4;
        add( btnResize, gbc );
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.gridwidth = 5;
        gbc.weighty = 0.1;
        gbc.fill = GridBagConstraints.BOTH;
        add( new JScrollPane( tblSpreadsheet ), gbc );
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weighty = 0;
        gbc.gridy = 2;
        add( lblStatus, gbc );
//        end of layout section
        
//        start of event section
        txtFormula.addKeyListener( new KeyAdapter() {

            @Override
            public void keyReleased( KeyEvent e ) {
                if( e.getKeyCode() == KeyEvent.VK_ENTER ) {
                    String strText = txtFormula.getText();
//                    try to tell is this is a formula or a file
                    if( !strText.isEmpty() ) {//it is a formula
                        tblSpreadsheet.setFormulaInSelectedCell( strText );
                    } else {//no input
                        JOptionPane.showMessageDialog( MainWindow.this, "Please provide a formula before hitting Enter." );
                    }
                }
            }
        });
        txtFormula.addFocusListener( new FocusAdapter() {

            @Override
            public void focusGained( FocusEvent e ) {
                //we need to stop cell editing when the text field gets the focus in order to be able to enter formulas in cells
                if( tblSpreadsheet.isEditing() ) {
                    tblSpreadsheet.getCellEditor().stopCellEditing();
                }
            }
        });
        btnOpen.addActionListener( new ActionListener() {

            @Override
            public void actionPerformed( ActionEvent e ) {
                JFileChooser jfc = createFileChooser();
                if( jfc.showOpenDialog( MainWindow.this ) == JFileChooser.APPROVE_OPTION ) {
                    fileCurrent = jfc.getSelectedFile();
                    if( fileCurrent == null ) {
                        return;
                    }
                    if( !fileCurrent.exists() ) {
                        setStatus( "File does not exist: " + fileCurrent.getAbsolutePath() );
                        return;
                    }
                    String strPath = fileCurrent.getAbsolutePath();
                    MainWindow.this.setTitle( strPath );
                    SpreadSheetCell[][] cells = null;
                    try {
                        cells = JDOMXMLReader.readXmlFile( strPath );
                    } catch( ParserConfigurationException ex ) {
                        setStatus( "An error occurred when reading file: " + strPath );
                    } catch( SAXException ex ) {
                        setStatus( "An error occurred when reading file: " + strPath );
                    } catch( IOException ex ) {
                        setStatus( "An error occurred when reading file: " + strPath );
                    }
                    if( cells != null ) {
                        tblSpreadsheet.setData( cells );
                    }
                    setStatus( "Opened file: " + strPath );
                }
            }
        });
        btnSave.addActionListener( new ActionListener() {

            @Override
            public void actionPerformed( ActionEvent e ) {
                JFileChooser jfc = createFileChooser();
                if( fileCurrent != null && fileCurrent.exists() ) {
                    jfc.setSelectedFile( fileCurrent );
                }
                if( jfc.showSaveDialog( MainWindow.this ) == JFileChooser.APPROVE_OPTION ) {
                    File file = jfc.getSelectedFile();
                    if( file != null ) {
                        String strPath = file.getAbsolutePath();
                        if( !strPath.endsWith( ".xml" ) && !strPath.endsWith( ".XML" ) ) {
                            strPath += ".xml";
                        }
                        SpreadSheet ssh = tblSpreadsheet.getSpreadSheet();
                        boolean bSuccess = false;
                        try {
                            bSuccess = ssh.saveSpreadSheet( strPath );
                        } catch( IOException ioe ) {}
                        if( bSuccess ) {
                            setStatus( "Saved file: " + strPath );
                        } else {
                            setStatus( "An error occurred when saving file: " + strPath );
                        }
                    }
                }
            }
        });
        btnResize.addActionListener( new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                ResizeTableDialog rtd = new ResizeTableDialog( tblSpreadsheet.getRowCount(), tblSpreadsheet.getColumnCount() - 1);
                rtd.setVisible( true );
                if( !rtd.isCanceled() ) {
                    int iRowCount = rtd.getRowCount();
                    int iColCount = rtd.getColCount();
                    SpreadSheetCell[][] cellsNew = new SpreadSheetCell[iRowCount][iColCount+1];
                    SpreadSheetCell[][] cellsOld = tblSpreadsheet.getSpreadSheet().getCells();
                    for( int i = 0; i < Math.min( cellsNew.length, cellsOld.length ); i++ ) {
                        System.arraycopy( cellsOld[i], 0, cellsNew[i], 0, Math.min( cellsNew[0].length, cellsOld[0].length ) );
                    }
                    tblSpreadsheet.setData( cellsNew );
                    setStatus( "The table has been resized to " + iRowCount + " rows and " + iColCount + " columns." );
                }
                rtd.dispose();
            }
        });
        //end of event section
        
        setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        pack();
        setExtendedState(MAXIMIZED_BOTH);
    }
    
    /**
     * Method for displaying messages in the status bar
     */
    public void setStatus( String strMessage ) {
        lblStatus.setText( "Status: " + strMessage );
    }
    
    public void setTextFieldContent( String str ) {
        txtFormula.setText( str );
    }
    
    private JFileChooser createFileChooser() {
        JFileChooser jfc = new JFileChooser();
        jfc.setFileFilter( new FileFilter() {

            @Override
            public boolean accept( File file ) {
                return file != null && ( file.getName().endsWith( ".xml" ) || file.getName().endsWith( ".XML" ) );
            }

            @Override
            public String getDescription() {
                return "Spreadsheet XML files";
            }
        });
        jfc.setFileSelectionMode( JFileChooser.FILES_ONLY );
        jfc.setMultiSelectionEnabled( false );
        if( fileCurrent != null ) {
            jfc.setCurrentDirectory( fileCurrent.getParentFile() );
        }
        return jfc;
    }
    
    private class ResizeTableDialog extends JDialog {
        
        private JSpinner spnRows = new JSpinner();
        private JSpinner spnCols = new JSpinner();
        private JButton btnOK = new JButton( "OK" );
        private JButton btnCancel = new JButton( "Cancel" );
        private boolean isCanceled = false;
        
        private ResizeTableDialog( int iRowCount, int iColCount ) {
            super( MainWindow.this, "Resize Table" );
            setModal( true );
            SpinnerNumberModel mdlRows = new SpinnerNumberModel( iRowCount, Table.ROW_COUNT, 1000, 1 );
            SpinnerNumberModel mdlCols = new SpinnerNumberModel( iColCount, Table.COL_COUNT, 702, 1 );
            spnRows.setModel( mdlRows );
            spnCols.setModel( mdlCols );
            setLayout( new GridBagLayout() );
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets( 5, 5, 5, 5 );
            gbc.anchor = GridBagConstraints.WEST;
            add( new JLabel( "Number of rows:" ), gbc );
            gbc.gridx = 1;
            gbc.anchor = GridBagConstraints.EAST;
            gbc.weightx = 0.1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add( spnRows, gbc );
            gbc.weightx = 0;
            gbc.fill = GridBagConstraints.NONE;
            gbc.gridy = 1;
            gbc.gridx = 0;
            add( new JLabel( "Number of columns:" ), gbc );
            gbc.gridx = 1;
            gbc.weightx = 0.1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add( spnCols, gbc );
            gbc.fill = GridBagConstraints.NONE;
            gbc.gridy = 2;
            gbc.gridx = 0;
            add( btnOK, gbc );
            gbc.weightx = 0;
            gbc.gridx = 1;
            add( btnCancel, gbc );
            btnCancel.addActionListener( new ActionListener() {

                @Override
                public void actionPerformed( ActionEvent e ) {
                    ResizeTableDialog.this.setVisible( false );
                    isCanceled = true;
                }
            });
            btnOK.addActionListener( new ActionListener() {

                @Override
                public void actionPerformed( ActionEvent e ) {
                    ResizeTableDialog.this.setVisible( false );
                }
            });
            pack();
            setLocationRelativeTo( MainWindow.this );
        }
        
        private int getRowCount() {
            return Integer.parseInt( spnRows.getValue().toString() );
        }
        
        private int getColCount() {
            return Integer.parseInt( spnCols.getValue().toString() );
        }
        
        private boolean isCanceled() {
            return isCanceled;
        }
    }
}